Skip to content

fix: TokenField RTL, IME composition, and range selection#10273

Open
devongovett wants to merge 20 commits into
mainfrom
tokenfield-fixes
Open

fix: TokenField RTL, IME composition, and range selection#10273
devongovett wants to merge 20 commits into
mainfrom
tokenfield-fixes

Conversation

@devongovett

Copy link
Copy Markdown
Member

Fixes many TokenField bugs found during testing.

  • Shift + Arrow key selection now uses native selection extension methods which preserve the anchor position. Extending to the left and then right moves back toward the anchor.
  • Cursor and selection movement now works properly with RTL and bidirectional text
  • Editing with an IME works much better. This is especially important on Android which does almost all text editing via composition events, even in english. The approach is to prevent React from re-rendering during composition and allow the browser to mutate the DOM. We track all modifications with a MutationObserver and revert them when composition ends, apply the updates to our model, and re-render with React. We do emit onChange events during composition so that autocomplete works properly, but only re-render if the value prop changes to a completely different value (e.g. selecting a completion). Tests are added in a separate suite that only runs in Chrome since Playwright doesn't support simulating composition events in other browsers.
  • Implements isDisabled, isReadOnly, and multiline (prop to be renamed)
  • Verifies copy/pasted JSON to ensure it matches the expected data model
  • Refactors and fixes the ComboBox example
  • Makes TokenSegmentList generic over the token value type instead of using any

@rspbot

rspbot commented Jun 26, 2026

Copy link
Copy Markdown

@rspbot

rspbot commented Jun 26, 2026

Copy link
Copy Markdown

@rspbot

rspbot commented Jun 27, 2026

Copy link
Copy Markdown

@rspbot

rspbot commented Jun 27, 2026

Copy link
Copy Markdown
## API Changes

react-aria-components

/react-aria-components:Provider

-Provider <A, B, C, D, E, F, G, H, I, J, K, L> {
+Provider <A, B, C, D, E, F, G, H, I, J, K, L, M> {
   children: ReactNode
-  values: ProviderValues<A, B, C, D, E, F, G, H, I, J, K, L>
+  values: ProviderValues<A, B, C, D, E, F, G, H, I, J, K, L, M>
 }

@react-spectrum/ai

/@react-spectrum/ai:TokenSegmentList

-TokenSegmentList {
+TokenSegmentList <T = any> {
   caretPosition: Position
-  constructor: (readonly Array<TokenFieldSegment>, TokenSegmentListOptions) => void
-  delete: (Position, Intl.Segmenter, any, any) => TokenSegmentList
-  deleteLine: (Position, any, any) => TokenSegmentList
+  constructor: (readonly Array<TokenFieldSegment<T>>, TokenSegmentListOptions) => void
+  delete: (Position, Intl.Segmenter, any, any) => this
+  deleteLine: (Position, any, any) => this
   endCoalescing: () => void
   findBoundaryWithSegmenter: (Position, Intl.Segmenter, any) => Position | null
   findLineBoundary: (Position, any) => Position | null
   findText: (Position, any, string | RegExp) => Position | null
-  insertToken: (Position) => TokenSegmentList
-  redo: () => TokenSegmentList
-  replaceRange: (Position, Position, string, any) => TokenSegmentList
-  replaceRangeWithSegments: (Position, Position, Array<TokenFieldSegment>, any) => TokenSegmentList
-  segments: readonly Array<TokenFieldSegment>
-  slice: (Position, Position) => TokenSegmentList
+  redo: () => this
+  replaceRange: (Position, Position, string, any) => this
+  replaceRangeWithSegments: (Position, Position, Array<TokenFieldSegment<T>>, any) => this
+  segments: readonly Array<TokenFieldSegment<T>>
+  slice: (Position, Position) => this
   toString: () => string
-  undo: () => TokenSegmentList
-  withCaretPosition: (Position) => TokenSegmentList
+  undo: () => this
+  withCaretPosition: (Position) => this
 }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants